#include "ruleio.h"

std::map<std::string, Vars::e_action> Vars::map_action;

Org::RuleIO::RuleIO()
{

}

Org::RuleIO::~RuleIO()
{

}

void Org::RuleIO::write_xml(const vector<Rule> rules, const string &filename = ORGANISER_RULES_PATH)
{
    FILE *fp;
    fp = fopen(filename.c_str(), "w");
    mxml_node_t *xml = mxmlNewXML("1.0");

    mxml_node_t *rulegroup = mxmlNewElement(xml, RULEGROUP_TAG);
    
    vector<Rule>::const_iterator rule_iter;

    for (rule_iter = rules.begin(); rule_iter < rules.end(); rule_iter++)
    {
        Rule rule = *rule_iter;
        mxml_node_t *rule_node = mxmlNewElement(rulegroup, RULE_TAG);

        mxmlElementSetAttrf(rule_node, ID_ATTR, "%d", rule.id);
        mxmlElementSetAttr(rule_node, NAME_ATTR, rule.rule_name.c_str());
        mxmlElementSetAttr(rule_node, TO_PATH_ATTR, rule.to_dir.string().c_str());

        string enabled = rule.enabled ? "t" : "f";
        mxmlElementSetAttr(rule_node, ENABLED_ATTR, enabled.c_str());
        mxmlElementSetAttr(rule_node, ACTION_ATTR, Vars::get_action(rule.action).c_str());

        if (rule.discogs_lookup.length() > 0)
        {
            mxmlElementSetAttr(rule_node, DISCOGS_ATTR, rule.discogs_lookup.c_str());
        }

        mxml_node_t *conditions_node = mxmlNewElement(rule_node, CONDITIONS_TAG);
        add_conditions(*rule.root_node, conditions_node);
    }

    mxmlSaveFile(xml, fp, MXML_NO_CALLBACK);
    fclose(fp);
}

void Org::RuleIO::add_conditions(Node node, mxml_node_t *parent)
{
    vector<Node>::iterator node_iter;
    for (node_iter = node.child_nodes.begin(); node_iter != node.child_nodes.end(); node_iter++)
    {
        Node child_node = *node_iter;

        mxml_node_t *condition_node = mxmlNewElement(parent, CONDITION_TAG);

        if (child_node.node_type == Vars::ORG_NODE_CONDITION)
        {
            mxmlElementSetAttr(condition_node, COND_TYPE_ATTR, "condition");
            mxml_node_t *search_node = mxmlNewElement(condition_node, SEARCH_TAG);
            mxmlNewText(search_node, 0, child_node.search_term.c_str());
            mxml_node_t *in_node = mxmlNewElement(condition_node, IN_TAG);
            if (child_node.search_type != Vars::ORG_NO_SEARCH_TYPE) {
                mxmlElementSetAttr(in_node, SEARCH_TYPE_ATTR, Vars::get_search_type(child_node.search_type).c_str());
            }
            mxmlNewText(in_node, 0, child_node.search_in.c_str());
        } else if (child_node.node_type == Vars::ORG_NODE_BOOL) {
            mxmlElementSetAttr(condition_node, COND_TYPE_ATTR, "bool");
            mxmlElementSetAttr(condition_node, COND_OP_ATTR, Vars::get_operation(child_node.operation).c_str());
        }

        add_conditions(child_node, condition_node);
    }

}

vector<Rule> Org::RuleIO::read_xml(const string &filename = ORGANISER_RULES_PATH)
{
    FILE *fp;
    mxml_node_t *tree;

    fp = fopen(filename.c_str(), "r");
    tree = mxmlLoadFile(NULL, fp, MXML_TEXT_CALLBACK);
    fclose(fp);

    mxml_node_t *rulegroup_node = mxmlFindElement(tree, tree, RULEGROUP_TAG,
                           NULL, NULL,
                           MXML_DESCEND);


    mxml_node_t *rule_node = mxmlFindElement(rulegroup_node, rulegroup_node, RULE_TAG,
                                             NULL, NULL,
                                             MXML_DESCEND_FIRST);

    vector<Rule> rules;

    while (rule_node != NULL)
    {
        Rule rule;
        rule.id = atoi(mxmlElementGetAttr(rule_node, ID_ATTR));
        rule.rule_name = string(mxmlElementGetAttr(rule_node, NAME_ATTR));
        rule.to_dir = string(mxmlElementGetAttr(rule_node, TO_PATH_ATTR));
        rule.enabled = mxmlElementGetAttr(rule_node, ENABLED_ATTR)[0] == TRUE_VAL;
        rule.action = Vars::get_action(string(mxmlElementGetAttr(rule_node, ACTION_ATTR)));

        const char *discogs_lookup = mxmlElementGetAttr(rule_node, DISCOGS_ATTR);
        if (discogs_lookup != NULL)
        {
            rule.discogs_lookup = string(discogs_lookup);
        }

        mxml_node_t *conditions_node = mxmlFindElement(rule_node, rule_node, CONDITIONS_TAG,
                                           NULL, NULL,
                                           MXML_DESCEND_FIRST);

        extract_conditions(rule.root_node, conditions_node);

        rule_node = mxmlFindElement(rule_node, rulegroup_node, RULE_TAG,
                                           NULL, NULL,
                                           MXML_NO_DESCEND);

        rules.push_back(rule);
    }

    return rules;
}

void Org::RuleIO::extract_conditions(Node *node, mxml_node_t *parent)
{
    mxml_node_t *condition_node = mxmlFindElement(parent, parent, CONDITION_TAG,
                                                  NULL, NULL,
                                                  MXML_DESCEND_FIRST);
    while (condition_node != NULL)
    {
        string condition_type(mxmlElementGetAttr(condition_node, COND_TYPE_ATTR));

        Node child_node;

        if (condition_type == COND_TYPE_COND)
        {
            child_node.node_type = Vars::ORG_NODE_CONDITION;
            mxml_node_t *search_node = mxmlFindElement(condition_node, condition_node, SEARCH_TAG,
                                                       NULL, NULL,
                                                       MXML_DESCEND);

            string search_string = parse_text_node(search_node);

            mxml_node_t *in_node = mxmlFindElement(condition_node, condition_node, IN_TAG,
                                                   NULL, NULL,
                                                   MXML_DESCEND);

            string in_string = parse_text_node(in_node);

            child_node.search_term = search_string;
            child_node.search_in = in_string;
            child_node.search_type = Vars::get_search_type(string(mxmlElementGetAttr(in_node, SEARCH_TYPE_ATTR)));
        }
        else if (condition_type == COND_TYPE_BOOL)
        {
            child_node.node_type = Vars::ORG_NODE_BOOL;
            child_node.operation = Vars::get_operation(string(mxmlElementGetAttr(condition_node, COND_OP_ATTR)));
            extract_conditions(&child_node, condition_node);
        }

        node->child_nodes.push_back(child_node);

        condition_node = mxmlFindElement(condition_node, parent, CONDITION_TAG,
                                         NULL, NULL,
                                         MXML_NO_DESCEND);
    }
}


string Org::RuleIO::parse_text_node(mxml_node_t *text_node)
{
    string text;

    mxml_node_t *text_node_data = mxmlWalkNext(text_node, text_node,
                                               MXML_DESCEND_FIRST);

    while (text_node_data != NULL)
    {
        text += text_node_data->value.text.string;
        text_node_data = mxmlWalkNext(text_node_data, text_node, MXML_NO_DESCEND);
        if (text_node_data != NULL)
        {
            text += " ";
        }
    }

    return text;
}

vector<RuleGroup> Org::RuleIO::parse_rulegroup_xml(const string &xml)
{
    vector<RuleGroup> rule_groups;

    mxml_node_t *tree = mxmlLoadString(NULL, xml.c_str(), MXML_TEXT_CALLBACK);

    mxml_node_t *rulegroups_node = mxmlFindElement(tree, tree, RULEGROUPS_TAG,
                                                   NULL, NULL,
                                                   MXML_DESCEND);

    mxml_node_t *rulegroup_node = mxmlFindElement(rulegroups_node, rulegroups_node, RULEGROUP_TAG,
                                                  NULL, NULL,
                                                  MXML_DESCEND_FIRST);

    while (rulegroup_node != NULL)
    {
        struct RuleGroup rule_group;

        mxml_node_t *author_node = mxmlFindElement(rulegroup_node, rulegroup_node, "author", NULL, NULL, MXML_DESCEND_FIRST);
        mxml_node_t *name_node = mxmlFindElement(rulegroup_node, rulegroup_node, "name", NULL, NULL, MXML_DESCEND_FIRST);
        mxml_node_t *description_node = mxmlFindElement(rulegroup_node, rulegroup_node, "description", NULL, NULL, MXML_DESCEND_FIRST);
        //mxml_node_t *date_inserted_node = mxmlFindElement(rulegroup_node, rulegroup_node, "date_inserted", NULL, NULL, MXML_DESCEND_FIRST);

        rule_group.id = atoi(mxmlElementGetAttr(rulegroup_node, ID_ATTR));
        rule_group.author = parse_text_node(author_node);
        rule_group.name = parse_text_node(name_node);
        rule_group.description = parse_text_node(description_node);
        //rule_group.date_inserted(parse_text_node(date_inserted_node));

        rulegroup_node = mxmlFindElement(rulegroup_node, rulegroups_node, RULEGROUP_TAG,
                                         NULL, NULL,
                                         MXML_NO_DESCEND);

        rule_groups.push_back(rule_group);
    }

    return rule_groups;
}

string Org::RuleIO::get_map_action_key(int val)
{
    std::string s_val;
    std::stringstream val_out;

    val_out << val;
    s_val = val_out.str();

    map<string, Vars::e_action>::iterator itr;

    for(itr = Vars::map_action.begin(); itr != Vars::map_action.end(); itr++)
    {
        int i = (int)itr->second;
        std::string s, k;
        std::stringstream out;
        out << i;
        s = out.str();

        if((int)itr->second == val)
        {
            return itr->first;
        }
    }
    return "UNDEFINED";
}
